import { $demoi18n } from '@/utils/lang';
import { isObject } from '@/utils/util.js';
import { ElMessage as Message } from 'element-plus';

export const getXvueRef = refName => {
  if (refName === 'Global'.toLowerCase()) { // 需要这样写，不然跟vite配置冲突
    return window._adoPremise;
  } else {
    const vm = window.xVueWeb.app.config.globalProperties?.xvGlobalRfs?.[refName];
    return vm?.$vmProxy || vm;
  }
};

export const isExpression = expression => {
  if (typeof expression !== 'string') {
    return false;
  }
  const _expression = expression.trim();
  const isExp = _expression.includes('{{') && _expression.includes('}}');
  return isExp;
};

// 检查是否循环依赖
const CircularMap = {};
const _hasCircularDependency = expression => {
  // 只在编排态判断，节省预览态内存
  if (!window.visualComp.isDesignView) {
    return false;
  }
  if (!expression || expression === '{{}}') {
    return false;
  }

  const nowTime = Date.now();
  if (!CircularMap[expression]) {
    CircularMap[expression] = {
      count: 1,
      time: nowTime,
      warned: false,
    };
    return false;
  }
  const item = CircularMap[expression];
  const diffTime = nowTime - item.time;
  if (diffTime < 100) {
    item.count ++;
  } else if (diffTime > 800) {
    item.warned = false;
    item.count = 1;
  } else {
    item.count = 1;
  }
  item.time = nowTime;
  if (item.count > 1000 && !item.warned) {
    item.warned = true;
    let msg = $demoi18n('low-code-util.hasCircularDependency');
    msg = msg.replace('%s', expression);
    setTimeout(() => {
      Message.error(msg);
    }, 500);
    return true;
  }

  return false;
};

// 降低圈复杂度
const _getLowCodeExpValSingle = _expression => {
  // 布尔
  let match = _expression.match(/^true|false$/);
  if (match) {
    return match[0] === 'true' ? true : false;
  }

  // 字符串
  match = _expression.match(/^'(.*)'$/);
  if (match) {
    return match[1];
  }

  // 数字
  match = _expression.match(/^(\d+(\.\d+)?)$/);
  if (match) {
    return Number(match[0]);
  }

  // 变量等判断 {{comp.a==comp.b}} {{comp.a===comp.b}}
  match = _expression.match(/^([\w\.]+)\s*(===?)\s*([^\s]+)$/);
  if (match) {
    const left = getLowCodeExpValSingle({ expression: `{{${match[1]}}}` });
    const right = getLowCodeExpValSingle({ expression: `{{${match[3]}}}` });
    if (match[2] === '===') {
      return left === right;
    }
    if (match[2] === '==') {
      return left == right;
    }
  }

  return '__noMatch__';
};

/**
 * 根据表达式获取值
 * @param  {string} expression 表达式，如：
 *                             '{{self.config.disName}}' // 本组件config.disName属性
 *                             '{{vs_button1.theme}}' // 别的组件（vs_button1）的theme值
 *                             '{{self.getList(table1.data)}}', // 本组件函数getList，表格组件数据table1.data作为入参
 *                             '{{table1.getList(table1.data)}}', // 表格组件函数getList，表格组件数据table1.data作为入参
 * @return {mixed}
 */
export const getLowCodeExpValSingle = ({ expression, vm = null }) => {
  if (!isExpression(expression)) {
    return expression;
  }

  if (_hasCircularDependency(expression)) {
    return expression;
  }

  let _expression = expression.trim();
  _expression = _expression.replace('{{', '').replace('}}', '');
  _expression = _expression.replace(/\[([^\]]+)\]/g, '.$1'); // 数组替换 a[0].name => a.0.name
  if (!_expression) {
    return expression;
  }

  const value = _getLowCodeExpValSingle(_expression);
  if (value !== '__noMatch__') {
    return value;
  }

  // 属性表达式，如 'vs_button1.theme'
  const paths = _expression.split('.');
  const rooterName = paths.shift(); // 命名空间： 可以是自身组件（self）, 别的组件(table1), 查询（ap1）
  const rooter = rooterName === 'self' ? (vm?.$vmProxy || vm) : getXvueRef(rooterName);

  if (!rooter) {
    return expression;
  }

  // 函数表达式，如 'seft.getList(table1.data)'
  if (_expression.includes('(')) {
    let [fun = '', prop = ''] = _expression.match(/[^\(\)]+/g);
    fun = getLowCodeExpValSingle({ expression: `{{${fun}}}` });
    prop = getLowCodeExpValSingle({ expression: `{{${prop}}}` });
    if (!fun) {
      return expression;
    }
    return fun(prop);
  }

  let val = rooter;
  let pObj = rooter;
  for (let i = 0; i < paths.length; i++) {
    const k = paths[i];
    const isLast = (i === paths.length - 1);

    if (vm) {
      vm.addLowCodeWatchs({ srcObj: pObj, srcKey: k });
    }
    if (val[k] === undefined) {
      if (isObject(val) && (k in val) && isLast) { // 不能用objHas函数判断，k不一定存在实例里面
        // 是最后一个，而且是对象的属性，不处理
      } else {
        val = expression;
        break;
      }
    }
    if (!val[k] && !isLast) {
      val = expression;
      break;
    }

    pObj = val;
    val = val[k];
  }

  return val;
};

export const getLowCodeExpVal = ({ expression, vm = null }) => {
  let val = expression;
  if (typeof val === 'string' && val.includes('{{')) {
    val = val.trim();
    const vals = [];
    const strVal = val.replace(/\{\{[^}]+\}\}/g, s => {
      const v = getLowCodeExpValSingle({ expression: s, vm });
      vals.push(v);
      return v;
    });

    if (vals.length === 1) {
      if (val.startsWith('{{') && val.endsWith('}}')) { // 单个{{}}时，可以返回非字符串值，如 {{btn1.a}} => true 或 123
        val = vals[0];
      } else if (val.startsWith('[{{') && val.endsWith('}}]')) { // 数组形式返回，如： [{{btn1.a}}] => [123]
        val = [vals[0]];
      } else {
        val = strVal;
      }
    } else { // 多个{{}}或有其他字符串拼接时，只能返回字符串，如：{{btn1.a}} hello {{pie1.b}} => 123 hello true
      val = strVal;
    }
  }
  return val;
};

export const executeExp = expression => {
  let _expression = expression.trim();
  const isExp = _expression.startsWith('{{') && _expression.endsWith('}}');
  if (!isExp) {
    return false;
  }

  _expression = _expression.replace('{{', '').replace('}}', '');
  if (!_expression) {
    return false;
  }

  // 赋值形式，如 {{api1.params.category=search.value}}'
  if (_expression.includes('=')) {
    let [left, right] = _expression.split('=');
    left = left.trim();
    right = right.trim();
    const rightVal = getLowCodeExpValSingle({ expression: `{{${right}}}` });
    const leftPaths = left.split('.');
    const refName = leftPaths.shift();
    const leftKey = leftPaths.pop();
    let leftObj = getXvueRef(refName);

    for (let i = 0; i < leftPaths.length; i++) {
      const k = leftPaths[i];
      leftObj = leftObj[k];

      if (!leftObj) {
        return false;
      }
    }
    leftObj[leftKey] = rightVal;
    return true;
  }

  // 函数形式，如 {{ap1.run()}}
  if (_expression.includes('(')) {
    let [fun = '', prop = ''] = _expression.match(/[^\(\)]+/g);
    fun = getLowCodeExpValSingle({ expression: `{{${fun}}}` });
    prop = getLowCodeExpValSingle({ expression: `{{${prop}}}` });
    if (!fun) {
      return false;
    }
    fun(prop);
    return true;
  }

  return true;
};

const _getElSelector = el => {
  if (el.className) {
    const excludes = [
      'extStyle_',
      'el-tooltip__trigger',
      'node-picker-hover',
      'node-picker-current',
      'isBorder', // 弹窗组件
      'row--hover', // 表格组件鼠标经过
      /^col_\d+$/, // 表格单元格
      /^tid_\d+$/, // 表格单元格
      /search[a-z\d-]{20}/, // 搜索组件放在模板下会失效
    ];
    let arr = [...el.classList].filter(n => {
      const isExcluded = excludes.some(n1 => {
        if (typeof n1 === 'string') {
          return n.includes(n1);
        }
        return n1.test(n);
      });
      return !isExcluded;
    });
    if (!arr.length) {
      return el.tagName.toLowerCase();
    }
    arr = Array.from(new Set(arr));
    let selector = `${el.tagName.toLowerCase()}.${arr.join('.')}`;
    selector = selector.replace('.[object.Object]', ''); // 历史代码componentStyleClass影响会生成一个对象
    return selector;
  }
  return el.tagName.toLowerCase();
};

// 是否弹层下面的元素
export const isPopupChild = (el, compEl, wid) => {
  if (!el) {
    return false;
  }
  let pEl = el.parentNode;
  let isPopC = false;
  while (pEl) {
    if (pEl === document.body || pEl === compEl) {
      break;
    }

    if (pEl.classList.contains(`extStyle_${wid}`)) { // 需要手工在popper-class上添加extStyle_xxx
      isPopC = true;
      break;
    }

    pEl = pEl.parentNode;
  }
  return isPopC;
};

export const getCssPath = (el, compEl, wid) => {
  if (!compEl.contains(el) && !isPopupChild(el, compEl, wid)) {
    return '';
  }
  let cssPath = _getElSelector(el);

  let pEl = el.parentNode;
  while (pEl) {
    if (pEl === document.body || pEl === compEl || pEl.classList.contains(`extStyle_${wid}`)) {
      break;
    }

    const pSelector = _getElSelector(pEl);
    cssPath = `${pSelector}>${cssPath}`;
    pEl = pEl.parentNode;
  }
  return cssPath;
};
